package server

import (
	"errors"
	"github.com/yeahyf/go_base/crypto"
	"github.com/yeahyf/go_base/ept"
	"github.com/yeahyf/go_base/log"
	"github.com/yeahyf/go_base/strutil"
	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/proto"
	"io"
	"jian-picture-service/biz"
	"jian-picture-service/utils"
	"net/http"
	"strconv"
	"strings"
	"time"
)

const (
	FBHeaderNid  = "x-fb-nid" // nid
	FBHeaderSign = "x-fb-sn"
	FBHeaderCT   = "x-fb-ct"

	HeaderFBBrowser = "x-fb-browser"

	HeadContentEncoding = "content-encoding" //gzip

	HeadContentType = "content-type" //type
	JSONType        = "application/json"
	Token           = "token"
)

// doPrepare 请求数据预处理
func doPrepare(r *http.Request, message biz.ReqMessage) int {
	//检查nid
	reqNid := getReqHeaderNid(r)
	if len(reqNid) != 36 {
		log.Errorf("error nid : %s", reqNid)
		return biz.NidErrorCode
	}

	//验证签名
	postData, err := io.ReadAll(r.Body)
	if err != nil {
		log.Errorf("couldn't get post data: %v", err)
		return biz.NetIOErrorCode
	}
	temp := make([]byte, 0, len(postData)+len(reqNid))
	temp = append(temp, postData...)
	temp = append(temp, reqNid...)
	reqSn := getReqHeaderSign(r)
	sn := crypto.MD54Bs(temp)
	if reqSn != sn {
		log.Errorf("signature mismatch nid %s, reqSn:%s,sn: %s", reqNid, reqSn, sn)
		return biz.SignErrorCode
	}

	encoding := r.Header.Get(HeadContentEncoding)
	//暂时只支持gzip
	if encoding == "gzip" {
		postData, err = strutil.Gunzip(postData)
		if err != nil {
			log.Errorf("couldn't gunzip source, %s, %v", reqNid, err)
			return biz.GzipErrorCode
		}
	}

	contentType := getReqHeaderCT(r)
	if contentType == JSONType {
		err = protojson.Unmarshal(postData, message)
		//err = json.Unmarshal(source, message)
	} else {
		err = proto.Unmarshal(postData, message)
	}
	if err != nil {
		log.Errorf("couldn't unmarshal source, %s %v", reqNid, err)
		return biz.ProtoUnMarshalErrCode
	}

	if log.IsDebug() {
		log.Debugf("req: %s, nid:%s", r.RequestURI, reqNid)
		log.Debugf("param: %s", message.DebugString())
	}

	return message.CheckID()
}

// doEnd 响应数据处理
func doEnd(r *http.Request, w http.ResponseWriter, message biz.RespMessage) {
	if log.IsDebug() {
		log.Debugf("resp: %s", message.DebugString())
	}
	var err error
	var pb []byte
	contentType := getReqHeaderCT(r)
	if contentType == JSONType {
		m := protojson.MarshalOptions{EmitUnpopulated: true}
		pb, err = m.Marshal(message)
	} else {
		pb, err = proto.Marshal(message)
	}
	if err != nil {
		log.Errorf("couldn't marshal message,%v,%v", err, message)
		w.WriteHeader(biz.ProtoMarshalErrCode)
		return
	}
	_, err = w.Write(pb)
	if err != nil {
		log.Errorf("write data error %v", err)
	}
	return
}

// checkError 判断错误类型并且返回
func checkError(err error, w http.ResponseWriter, r *http.Request) {
	var myErr *ept.Error
	if errors.As(err, &myErr) {
		if myErr.Code < 999 { // http现在支持的最大错误码
			w.WriteHeader(int(myErr.Code))
		} else {
			w.WriteHeader(http.StatusInternalServerError)
		}
		w.Write([]byte(myErr.Message))
		return
	}
	w.WriteHeader(http.StatusInternalServerError)
	w.Write([]byte(err.Error()))
}

func getReqHeaderNid(r *http.Request) string {
	reqNid := r.Header.Get(FBHeaderNid)
	return reqNid
}

func getReqHeaderSign(r *http.Request) string {
	reqSign := r.Header.Get(FBHeaderSign)
	return reqSign
}

func getReqHeaderCT(r *http.Request) string {
	contentType := r.Header.Get(FBHeaderCT)
	if contentType == "" {
		contentType = r.Header.Get(HeadContentType)
	}
	return contentType
}

// 解决跨域问题
func corsMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("content-type", r.Header.Get("content-type"))
		w.Header().Set("Access-Control-Allow-Credentials", "true")
		w.Header().Set("Access-Control-Allow-Methods", "*")
		w.Header().Set("Access-Control-Allow-Origin", "http://localhost:5173")
		w.Header().Set("access-control-max-age", "1728000")
		w.Header().Set("Access-Control-Allow-Headers",
			strings.Join([]string{FBHeaderCT, FBHeaderSign, FBHeaderNid,
				HeaderFBBrowser, HeadContentType, HeadContentEncoding, Token}, ","))

		// 如果是OPTIONS预检请求，直接返回，不执行下一步处理
		if strings.EqualFold(r.Method, "OPTIONS") {
			w.WriteHeader(http.StatusOK)
			return
		}

		// 执行下一步处理
		next.ServeHTTP(w, r)
	})
}

func calcTaskTime(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		var oldTime int64
		oldTime = time.Now().UnixMilli()
		next.ServeHTTP(w, r)
		if log.IsDebug() {
			log.Debugf("req %s task time: %d ms", r.RequestURI, time.Now().UnixMilli()-oldTime)
		}
	})
}

func checkLogin(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		jwt := r.Header.Get(Token)
		info, err := utils.ParseToken(jwt)
		if err != nil {
			checkError(ept.New(http.StatusUnauthorized, "token不可用"), w, r)
			return
		}
		r.Header.Set("user_id", strconv.Itoa(int(info.UserId)))
		next.ServeHTTP(w, r)
	})
}

// 需要验证token身份的接口
func authInterface(mux *http.ServeMux, path string, handler http.HandlerFunc) {
	mux.Handle(path, checkLogin(handler))
}
